﻿using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace HMACDemo
{
    class Program
    {
        static void Main(string[] args)
        {  //使用SHA1的HMAC

            HMAC hmac = (HMAC)HMAC.Create(algName: "HMACSHA1");



            //源数据

            var data = new byte[] { 1, 2, 3, 4, 5, 6, 7 };

            //密钥

            var key = new byte[100];

            var keyStr = Guid.NewGuid().ToString();

            keyStr = "sp3Z2GB4tLKtkGFLTsQwxCFJ";

            key = Encoding.ASCII.GetBytes(keyStr);

            //创建一个随即密钥

            using (var rng = RandomNumberGenerator.Create())

            {

                // rng.GetBytes(key);

            }

            //对数据进行签名

            var signedData = SignData(key, data, hmac);

            //输出数据

            PrintData(signedData, hmac);

            //认证

            Console.WriteLine(VerityData(key, signedData, hmac) ? "data valid" : "data changed");



            //故意修改数据（将源数据的5修改成4）

            signedData[(hmac.HashSize >> 3) + 4] = 4;

            //输出数据

            PrintData(signedData, hmac);

            //认证

            Console.WriteLine(VerityData(key, signedData, hmac) ? "data valid" : "data changed");

            Console.WriteLine("Hello World!");
        }

        static void PrintData(byte[] data, HMAC alg)

        {

            Console.WriteLine("哈希值: {0}\n文件值: {1}",

                BitConverter.ToString(data.Take(alg.HashSize >> 3).ToArray()),

                BitConverter.ToString(data.Skip(alg.HashSize >> 3).ToArray()));

        }

        //数据签名

        static byte[] SignData(byte[] key, byte[] data, HMAC alg)

        {

            //设置密钥

            alg.Key = key;

            //计算哈希值

            var hash = alg.ComputeHash(data);

            //返回具有签名的数据（哈希值+数组本身）

            return hash.Concat(data).ToArray();

        }

        //数据认证

        static bool VerityData(byte[] key, byte[] data, HMAC alg)

        {

            //提取收到的哈希值

            var receivedHash = data.Take(alg.HashSize >> 3);

            //提取数据本身

            var dataContent = data.Skip(alg.HashSize >> 3).ToArray();

            //设置密钥

            alg.Key = key;

            //计算数据哈希值和收到的哈希值

            var computedHash = alg.ComputeHash(dataContent);

            //如果相等则数据正确

            return receivedHash.SequenceEqual(computedHash);

        }
    }
}
